package com.gitee.shoes.crawler.core.dewu;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.Method;
import cn.hutool.json.JSONUtil;
import com.gitee.shoes.crawler.bean.*;
import com.gitee.shoes.crawler.core.AbstractApiRequest;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author LYH
 */
@Slf4j
public class DewuApiRequest extends AbstractApiRequest {

    /**
     * 签名时使用到的莫名串
     */
    private static final String SIGN_USED = "19bc545a393a25177083d4a748807cc0";

    private interface Path {
        String host = "app.poizon.com";
        String base = "https://" + host;
        String detail = base + "/api/v1/h5/index/fire/flow/product/detail";
        String list = base + "/api/v1/h5/search/fire/search/list";
        String buy_now_info = base + "/api/v1/h5/inventory/price/h5/queryBuyNowInfo";
        String last_sold_list = base + "/api/v1/h5/commodity/fire/last-sold-list";
    }

    @Override
    public ApiProductList productList(String keyword, Integer page, Integer limit, String nextKey, OrderMode orderMode) {
        if (page == null || page < 0) {
            page = 0;
        }
        if (limit == null || limit <= 0 || limit > 20) {
            // 得物限制最大为0
            limit = 20;
        }
        int sortType = 0, sortMode = 0;
        if (OrderMode.sales.equals(orderMode)) {
            sortType = 1;
        } else if (OrderMode.priceAse.equals(orderMode)) {
            sortType = 2;
        } else if (OrderMode.priceDesc.equals(orderMode)) {
            sortType = 2;
            sortMode = 1;
        } else if (OrderMode.news.equals(orderMode)) {
            sortType = 3;
        }
        Dict paramDict = Dict.of("title", keyword, "page", page, "sortType", sortType, "sortMode", sortMode, "limit", limit
                , "showHot", 1, "isAggr", 1);
        String sign = sign(paramDict);
        System.out.println(sign);
        paramDict.set("sign", sign);
        String params = MapUtil.sortJoin(paramDict, "&", "=", false);
        System.out.println(params);
        String url = StrUtil.format("{}?{}", Path.list, params);
        DewuResp<ItemData> duResp = request(Method.GET, url, null, new TypeReference<DewuResp<ItemData>>() {
        });
        ItemData data = duResp.getData();

        Integer total = data.getTotal();
        Integer pageNum = data.getPage();
        List<ItemData_ProductList> productList = data.getProductList();
        List<ApiProduct> productItems = productList.stream().map(e -> new ApiProduct()
                .setMainId(Convert.toStr(e.getSpuId()))
                .setLogoUrl(e.getLogoUrl()).setTitle(e.getTitle())
                .setMinSalePrice(handlerPrice(e.getMinSalePrice())).setSoldInfo(Convert.toStr(e.getSoldNum()))
                .setArticle(e.getArticleNumber())).collect(Collectors.toList());
        ApiProductList productItemList = new ApiProductList().setTotal(total).setPage(pageNum).setItemList(productItems);
        return productItemList;
    }

    @Override
    public ApiProduct productDetail(String mainId, String styleId) {
        Dict paramDict = Dict.of("spuId", mainId, "productSourceName", null, "propertyValueId", null);
        String sign = sign(paramDict);
        paramDict.put("sign", sign);
        DewuResp<DetailData> duResp = request(Method.POST, Path.detail, JSONUtil.toJsonStr(paramDict),
                new TypeReference<DewuResp<DetailData>>() {
                });
        DetailData data = duResp.getData();
        DetailData_Detail detail = data.getDetail();
        List<String> imageList = data.getImage().getSpuImage().getImages().stream().map(e -> e.getUrl())
                .collect(Collectors.toList());

        List<DetailData_BaseProperties_BrandList> brandList = data.getBaseProperties().getBrandList();
        String brandName = null;
        if (!brandList.isEmpty()) {
            brandName = brandList.get(0).getBrandName();
        }

        Map<Long, DetailData_Skus> propertyValueIdSkuMap = new HashMap<>();
        List<DetailData_Skus> skus = data.getSkus();
        skus.forEach(e -> {
            List<DetailData_Skus_Properties> properties = e.getProperties();
            if (!properties.isEmpty()) {
                properties.forEach(r -> propertyValueIdSkuMap.put(r.getPropertyValueId(), e));
            }
        });

        List<ApiProductSize> productSizeList = new ArrayList<>();
        List<DetailData_SaleProperties_List> saleList = data.getSaleProperties().getList();
        int maxLevel = saleList.stream().mapToInt(DetailData_SaleProperties_List::getLevel).max().getAsInt();
        saleList.forEach(e -> {
            if (e.getLevel().equals(maxLevel)) {
                Long propertyValueId = e.getPropertyValueId();
                String size = e.getValue();
                ApiProductSize productSize = new ApiProductSize().setPropertyValueId(Convert.toStr(propertyValueId))
                        .setSize(size);
                DetailData_Skus sku = propertyValueIdSkuMap.get(propertyValueId);

                // 可能存在码数找不到sku的情况
                if (sku != null) {
                    Long skuId = sku.getSkuId();
                    productSize.setSkuId(Convert.toStr(skuId));
                    productSizeList.add(productSize);
                }
            }
        });

        DetailData_LastSoldList lastSoldList = data.getLastSoldList();

        ApiProduct productDetail = new ApiProduct()
                .setMainId(mainId)
                .setLogoUrl(detail.getLogoUrl()).setTitle(detail.getTitle())
                .setAuthPrice(Convert.toStr(detail.getAuthPrice())).setSellDate(detail.getSellDate())
                .setArticle(handlerArticle(detail.getArticleNumber())).setBrandName(brandName)
                .setSizeList(productSizeList)
                .setImageList(imageList).setSoldInfo(Convert.toStr(lastSoldList.getCount()));

        return productDetail;
    }

    @Override
    public ApiProductBuyNowList productBuyNowList(String mainId, String styleId, List<ApiProductSize> productSizeList) {
        Dict paramDict = Dict.of("spuId", mainId);
        String sign = sign(paramDict);
        paramDict.put("sign", sign);
        DewuResp<BuyNowData> duResp = request(Method.POST, Path.buy_now_info, JSONUtil.toJsonStr(paramDict),
                new TypeReference<DewuResp<BuyNowData>>() {
                });

        BuyNowData data = duResp.getData();

        List<BuyNowData_SkuInfoList> skuInfoList = data.getSkuInfoList();
        Map<Long, ApiProductSize> skuIdProductSizeMap = productSizeList.stream().collect(Collectors.toMap(
                e -> Convert.toLong(e.getSkuId()), Function.identity()));
        List<ApiProductBuyNow> productBuyNows = new ArrayList<>();
        skuInfoList.forEach(skuInfo -> {
            ApiProductSize productSize = skuIdProductSizeMap.get(skuInfo.getSkuId());
            if (productSize != null) {
                String size = productSize.getSize();
                List<BuyNowData_SkuInfoList_TradeChannelInfoList> tradeChannelInfoList = skuInfo.getTradeChannelInfoList();
                tradeChannelInfoList.removeIf(tradeChannelInfo -> tradeChannelInfo.getPrice() == null &&
                        tradeChannelInfo.getPrice() == 0);
                tradeChannelInfoList.forEach(tradeChannelInfo -> {
                    ApiProductBuyNow productBuyNow = new ApiProductBuyNow().setSize(size)
                            .setMinSalePrice(handlerPrice(tradeChannelInfo.getPrice()))
                            .setTradeTypeName(tradeChannelInfo.getTradeTypeName());
                    if (tradeChannelInfo.getTradeType().equals(95)) {
                        productBuyNow.setTradeType(ApiTradeType.leave);
                    }
                    productBuyNows.add(productBuyNow);
                });
            }
        });
        return new ApiProductBuyNowList().setBuyNowList(productBuyNows);
    }

    /**
     * @param mainId
     * @param styleId
     * @param limit   得物最多100
     * @param lastId  为空时查最新
     * @return
     */
    @Override
    public ApiProductLastSoldList productLastSoldList(String mainId, String styleId, Integer limit, String lastId) {
        if (limit == null || limit <= 0 || limit > 100) {
            limit = 100;
        }
        Dict paramDict = Dict.of("spuId", mainId, "limit", limit, "lastId", lastId, "sourceApp", "app");
        String sign = sign(paramDict);
        paramDict.put("sign", sign);
        DewuResp<LastSoldData> duResp = request(Method.POST, Path.last_sold_list, JSONUtil.toJsonStr(paramDict),
                new TypeReference<DewuResp<LastSoldData>>() {
                });
        LastSoldData data = duResp.getData();
        Long lastId1 = data.getLastId();
        List<LastSoldData_List> lastSoldDataLists = data.getList();
        List<ApiProductLastSold> productLastSolds = lastSoldDataLists.stream().map(e -> {
            ApiProductLastSold apiProductLastSold = new ApiProductLastSold()
                    .setTime(handlerFormatTime(e.getFormatTime())).setPrice(handlerPriceToLong(e.getPrice())).setSize(e.getPropertiesValues());
            apiProductLastSold.setSoldId(createSoldId(apiProductLastSold, e));
            return apiProductLastSold;
        }).collect(Collectors.toList());
        return new ApiProductLastSoldList().setLastId(Convert.toStr(lastId1)).setLastSoldList(productLastSolds);
    }

    private static String sign(Map<String, Object> paramMap) {
        paramMap.forEach((k, v) -> {
            if (v == null) {
                paramMap.put(k, StrUtil.EMPTY);
            }
        });
        String s = MapUtil.sortJoin(paramMap, StrUtil.EMPTY, StrUtil.EMPTY, false, SIGN_USED);
        return SecureUtil.md5(s);
    }

    @Override
    protected void fillRequest(HttpRequest request) {
        request.header("AppId", "wxapp");
        request.header("Wxapp-Login-Token", "");
        request.header("appVersion", "4.4.0");
        request.header("platform", "h5");
    }

    private String createSoldId(ApiProductLastSold lastSold, LastSoldData_List soldData) {
        Long price = lastSold.getPrice();
        String size = lastSold.getSize();
        String userName = soldData.getUserName();
        return SecureUtil.md5(StrUtil.format("{}{}{}", userName, size, price));
    }

    private String handlerPrice(Long price) {
        if (price != null) {
            price = price / 100;
        }
        return Convert.toStr(price);
    }

    private Long handlerPriceToLong(Long price) {
        if (price != null) {
            price = price / 100;
        }
        return price;
    }

    private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");

    private LocalDateTime handlerFormatTime(String formatTime) {
        if ("刚刚".equals(formatTime)) {
            return LocalDateTime.now();
        }
        List<String> group0 = ReUtil.findAllGroup0(NUMBER_PATTERN, formatTime);
        int size = group0.size();
        if (size == 1) {
            Integer s = Convert.toInt(group0.get(0));
            LocalDateTime now = LocalDateTime.now();
            if (formatTime.contains("分钟前")) {
                return now.minusMinutes(s);
            } else if (formatTime.contains("小时前")) {
                return now.minusHours(s);
            } else if (formatTime.contains("天前")) {
                return now.minusDays(s);
            }
            return null;
        } else {
            Integer y = null;
            Integer m = null;
            Integer d = null;
            if (size == 2) {
                y = DateUtil.date().year();
                m = Convert.toInt(group0.get(0));
                d = Convert.toInt(group0.get(1));
            } else if (size == 3) {
                y = Convert.toInt(group0.get(0));
                m = Convert.toInt(group0.get(1));
                d = Convert.toInt(group0.get(2));
            }
            if (y == null) {
                return null;
            }
            return LocalDateTime.of(y, m, d, 0, 0);
        }
    }

}
